package com.google.code.p.localspinner;
import static com.google.code.p.localspinner.StaticMemory.CENTER;
import static com.google.code.p.localspinner.StaticMemory.COLORS;
import static com.google.code.p.localspinner.StaticMemory.FRAMES;
import static com.google.code.p.localspinner.StaticMemory.TXT_BITMAP;
import static com.google.code.p.localspinner.StaticMemory.WHEEL;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;

import com.google.code.p.localspinner.model.Item;
import com.google.code.p.localspinner.model.ItemList;

class WheelView extends SurfaceView implements SurfaceHolder.Callback{
    
    
    private static final String TITLE = "title";
    private static final String BODY = "body";

    private TextView titleText;
    private TextView bodyText;

    /** The thread that actually draws the animation */
    private WheelThread thread;
    private Drawable bg;
    private AudioManager audioManager;
//    private SoundPool soundPool;
    
    private boolean mute = false;
    private int soundeffect = 0;
    
    public WheelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // register our interest in hearing about changes to our surface
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);
        bg = getResources().getDrawable(R.drawable.bg3);

        // create thread only; it's started in surfaceCreated()
        thread = new WheelThread(holder, context, new Handler() {
            @Override
            public void handleMessage(Message m) {
                titleText.setText(m.getData().getString(TITLE));
                bodyText.setText(m.getData().getString(BODY));
            }
        });
        
        //get the user preferences
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
        mute = settings.getBoolean("mute", false);
        
        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        soundeffect = settings.getString("sound_effect", "Click").equals("Click")? R.raw.click12a : R.raw.ping;
        
        setFocusable(true); // make sure we get key events
    }

    /**
     * Fetches the animation thread corresponding to this View.
     * 
     * @return the animation thread
     */
    public WheelThread getThread() {
        return thread;
    }


    /**
     * Installs a pointer to the text view used for messages.
     */
    public void setTextView(TextView titleView, TextView bodyView) {
        this.titleText = titleView;
        this.bodyText = bodyView;
    }

    /* Callback invoked when the surface dimensions change. */
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        thread.setSurfaceSize(width, height);
    }
    
    /*
     * Callback invoked when the Surface has been created and is ready to be
     * used.
     */
    public void surfaceCreated(SurfaceHolder holder) {
        // start the thread here so that we don't busy-wait in run()
        // waiting for the surface to be created
        thread.setRunning(true);
        thread.start();
    }

    /*
     * Callback invoked when the Surface has been destroyed and must no longer
     * be touched. WARNING: after this method returns, the Surface/Canvas must
     * never be touched again!
     */
    public void surfaceDestroyed(SurfaceHolder holder) {
        // we have to tell thread to shut down & wait for it to finish, or else
        // it might touch the Surface after we return and explode
        boolean retry = true;
        thread.setRunning(false);
        while (retry) {
            try {
                thread.join();
                retry = false;
            } catch (InterruptedException e) {
            }
        }
    }

    
    
    class WheelThread extends Thread {

        private int mCanvasHeight = 1;
        private int mCanvasWidth = 1;

        /** Message handler used by thread to interact with TextView */
        private Handler mHandler;
        
        /** Indicate whether the surface has been created & is ready to draw */
        private boolean mRun = false;
        /** Handle to the surface manager object we interact with */
        private SurfaceHolder mSurfaceHolder;

        /** X of lander center. */
        private double mX;
        /** Y of lander center. */
        private double mY;
        
        private int mWheelWidth;
        private int mWheelHeight;


        private ItemList itemLists = null;
        private static final float SPEED = 0;
        private static final float DAMPING_FACTOR = 0.93f;
        private static final float MAGNET = .065f;
        
        
        private float deg = 0f;
        private float vr = SPEED;
        private int nbrWedges = 12;
        private int slot = 0;
        private long mLastTime = System.currentTimeMillis();

        private double lastAngleFromCenter = 0f;
        private float centerX = 160f, centerY = 230f;
        private double da = 0;
        
        public WheelThread(SurfaceHolder surfaceHolder, Context ctx,
                Handler handler) {
          
          // get handles to some important objects
          mSurfaceHolder = surfaceHolder;
          mHandler = handler;
          // Use the regular lander image as the model size for all sprites
          mWheelWidth = WHEEL.getIntrinsicWidth();
          mWheelHeight = WHEEL.getIntrinsicHeight();

          // initial show-up of lander (not yet playing)
          mX = 160;
          mY = 180;
        }


        @Override
        public void run() {
            while (mRun) {
                Canvas c = null;
                try {
                    c = mSurfaceHolder.lockCanvas(null);
                    synchronized (mSurfaceHolder) { 
                        updatePhysics();
                        doDraw(c);
                    }
                } finally {
                    // do this in a finally so that if an exception is thrown
                    // during the above, we don't leave the Surface in an
                    // inconsistent state
                    if (c != null) {
                        mSurfaceHolder.unlockCanvasAndPost(c);
                    }
                }
            }
        }

        /**
         * Dump game state to the provided Bundle. Typically called when the
         * Activity is being suspended.
         * 
         * @return Bundle with this view's state
         */
        public Bundle saveState(Bundle map) {
            return map;
        }

        /**
         * Used to signal the thread whether it should be running or not.
         * Passing true allows the thread to run; passing false will shut it
         * down if it's already running. Calling start() after this was most
         * recently called with false will result in an immediate shutdown.
         * 
         * @param b true to run, false to shut down
         */
        public void setRunning(boolean b) {
            mRun = b;
        }
        
        public void resetVr(){
          vr = SPEED;
        }

        
        public void onDown(MotionEvent e){
          double myX = e.getX() - centerX;
          double myY = e.getY() - centerY;
          lastAngleFromCenter = Math.atan2(myY, myX) * 180 / Math.PI;
        }
        
        
        public void onTrackball(MotionEvent event){
            double myX = event.getX();
            double myY = event.getY();
            double angleFromCenter = (float)(Math.atan2(myY, myX) * 180 / Math.PI);
            vr = (float)angleFromCenter*0.75f;
        }
        
        public void onScroll(MotionEvent e1, MotionEvent e2,
              float distanceX, float distanceY){
          double myX = e2.getX() - centerX;
          double myY = e2.getY() - centerY;
          double angleFromCenter = (float)(Math.atan2(myY, myX) * 180 / Math.PI);
          vr =  (float)(angleFromCenter - lastAngleFromCenter);
          if(vr > 180){
            vr -= 360f;
          }
          if(vr < -180){
            vr += 360;
          }
          vr *= 30;
          lastAngleFromCenter = angleFromCenter;
        }
        

        public void setText(CharSequence title, CharSequence body){
          synchronized(mSurfaceHolder){
            Message msg = mHandler.obtainMessage();
            Bundle b = new Bundle();
            b.putCharSequence(TITLE, title);
            b.putCharSequence(BODY, body);
            msg.setData(b);
            mHandler.sendMessage(msg);
          }
        }

        /* Callback invoked when the surface dimensions change. */
        public void setSurfaceSize(int width, int height) {
            // synchronized to make sure these all change atomically
            synchronized (mSurfaceHolder) {
                mCanvasWidth = width;
                mCanvasHeight = height;
            }
        }

        
        /**
         * Draws the ship, fuel/speed bars, and background to the provided
         * Canvas.
         */
        private void doDraw(Canvas canvas) {

          
          int[] color = COLORS[getSlot()];
          
          if(vr == 0){
            bg.setBounds(new Rect(0, 0, mCanvasWidth, mCanvasHeight));
            bg.draw(canvas);
          }else{
              canvas.drawRGB(color[0], color[1], color[2]);
          }

            int yTop = mCanvasHeight - ((int) mY + mWheelHeight / 2);
            int xLeft = (int) mX - mWheelWidth / 2;

            canvas.save();
            canvas.rotate(deg, (float) mX, mCanvasHeight
                    - (float) mY);
            
            WHEEL.setBounds(xLeft, yTop, xLeft + mWheelWidth, yTop
                  + mWheelHeight);
            WHEEL.draw(canvas);
            
            if(vr != 0){
              FRAMES[getSlot()].setBounds(xLeft, yTop, xLeft + mWheelWidth, yTop
                  + mWheelHeight);
              FRAMES[getSlot()].draw(canvas);
              Item item = itemLists.getItem(getSlot());
              setText(item.getTitle(), item.getBody());
            }
            TXT_BITMAP.setBounds(xLeft, yTop, xLeft + mWheelWidth, yTop
                + mWheelHeight);
            TXT_BITMAP.draw(canvas);
            canvas.restore();
            CENTER.setBounds(xLeft, yTop, xLeft + mWheelWidth, yTop
                    + mWheelHeight);
            CENTER.draw(canvas);
        }

        public int getSlot() {
          return slot%12;
        }
        
        public void setItemLists(ItemList itemLists) {
          this.itemLists = itemLists;
        }
                
        /**
         * Figures the lander state (x, y, fuel, ...) based on the passage of
         * realtime. Does not invalidate(). Called at the start of draw().
         * Detects the end-of-game and sets the UI to the next state.
         */
        private void updatePhysics() {
            long now = System.currentTimeMillis();
            
            float elapsed = (now - mLastTime) / 1000.0f;
            mLastTime = now;

            slot =  Math.round((deg * nbrWedges)/360);
            int curSlot = slot*360/nbrWedges;
            if(slot < 0){
              slot += 11;
            }
            
            slot = 15 - slot;
            if(slot> 11){
                slot -= 12;
            }
            
            slot = Math.abs(slot);
            
            deg += (vr * elapsed);
            if(deg>= 360f){
                deg -= 360f;
            }
            if(deg < 0f){
                deg += 360f;
            }
            

            if(!mute && da != curSlot){
                int volume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
                MediaPlayer mp = MediaPlayer.create(WheelView.this.getContext(), soundeffect);
                mp.setVolume(volume, volume);
                mp.setOnCompletionListener(new OnCompletionListener(){

                  @Override
                  public void onCompletion(MediaPlayer mp) {
                    mp.stop();
                    mp.release();
                  }

                });
                mp.start();
            }

            da = curSlot;

            double delta = da - deg;
            vr += delta*MAGNET;
            vr *= DAMPING_FACTOR;

        }
    }

}
